/*
 * UART1.c
 *
 * Created: 6.11.2013 8:27:47
 *  Author: Elektronik
 */ 

#include "UART1.h"
#include "util.h"
#include <avr/io.h>
#include <avr/interrupt.h>

//=================================================================================================================================================
#ifdef USE_UART1

static uint8_t tx_buffer[256] __attribute__((section("bss"), aligned(256))); // allow high optimization
static uint8_t tx_w_index __attribute__((section("bss")));
static uint8_t tx_r_index __attribute__((section("bss")));

static uint8_t rx_buffer[256] __attribute__((section("bss"), aligned(256))); // allow high optimization
static uint8_t rx_w_index __attribute__((section("bss")));
static uint8_t rx_r_index __attribute__((section("bss")));

//=======================================================================================
// inicializace serioveho rozhrani
// param ubbr - delitel pro generovani prenosove rychlosti
//=======================================================================================
OPTIMIZE_SIZE void UART1_begin(uint16_t ubr)
{
	tx_r_index = tx_w_index = rx_r_index = rx_w_index = 0;
	
	UBRR1 = ubr;
	
	// async mode, no parity, 1 stop bit,  8 data bits
	UCSR1C = (1 << UCSZ11) | (1 << UCSZ10);
	
	// use double speed
	UCSR1A = (1 << U2X1);
	
	// enable receive and send reg empty interrupt, enable receive and send interface
	UCSR1B = (1 << RXCIE1) | (1 << UDRIE1) | (1 << RXEN1) | (1 << TXEN1);
}

//=======================================================================================
// ukonceni prace serioveho rozhrani
//=======================================================================================
OPTIMIZE_SIZE void UART1_end()
{
	
	#warning "Not full impelemented"
	UCSR1A = 0;
	UCSR1B = 0;
}

//=======================================================================================
// odeslani jednoho bytu pres seriovou linku
//=======================================================================================
// 18 - cycles
NAKED_FUNC void UART1_write_byte(uint8_t c)
{
	asm volatile (
		"lds	r30, %[wip]				\n\t"	// load write index
		"mov	r25, r30				\n\t"	// and next write address
		"inc	r25						\n\t"
		
		"wait%=:						\n\t"
		"lds	r23, %[rip]				\n\t"	// may be changed
		"cp		r25, r23				\n\t"	
		"breq	wait%=					\n\t"	// wait if buffer is full

		"ldi	r31, hi8(%[buffer])		\n\t"
		"st		Z, r24					\n\t"	// store character to buffer
		
		"cli							\n\t"
		"sts	%[wip], r25				\n\t"	// and store next index, it now be used in interrupt
		"lds	r24, %[ctrl]			\n\t"	// load UCSR1B
		"ori	r24, (1 << 5)			\n\t"	// enable UDRE interrupt
		"sts	%[ctrl], r24			\n\t"	// 
		"sei							\n\t"
		:
		: [wip] "i" (&tx_w_index), [rip] "i" (&tx_r_index), [buffer] "i" (tx_buffer), [ctrl] "i" (_SFR_MEM_ADDR(UCSR1B)) 
		:);
}


//=======================================================================================
//
// odeslani bufferu dat pres seriovou linku
//
//=======================================================================================
// R31:R30	- ZH:ZL address of ring buffer
// R27:R26	- XH:XL address of source data
// R25		- read index and temp reg
// R24		- next write index
// R22		- length of data
//==================================
// init		  -	5  cycles
// store loop - 20 cycles per byte
//==================================
__attribute__((naked)) void UART1_write_bytes(uint8_t * ptr, uint8_t length)
{
	asm volatile (
		// copy pointer address to X reg, it may be used in indirect addressing
		"movw	r26, r24				\n\t"	
		
		// load	write address into Z and aux reg 24
		"ldi	r31, hi8(%[buffer])		\n\t"	// remain same for all times
		"lds	r30, %[wip]				\n\t"
		"mov	r24, r30				\n\t"	
		
		// init new store operation	
		"begin%=:						\n\t"
		"inc	r24						\n\t"	// R24 = write index + 1		
		
		// wait until buffer is full
		"wait%=:						\n\t"	
		"lds	r25, %[rip]				\n\t"	
		"cp		r24, r25				\n\t"
		"breq	wait%=					\n\t"
		
		// load data and store it to buffer		
		"ld		r25, X+					\n\t"	// load byte from source
		"st		Z, r25					\n\t"	// store byte to ring buffer
		
		// new write index to ZL and store it
		"mov	r30, r24				\n\t"	// write index = next write index
		
		// atomic update write index and interrupt enable bit
		"cli							\n\t"
		"sts	%[wip], r30				\n\t"	// store write_index
		"lds	r25, %[ctrl]			\n\t"	// enable UDRIE interrupt	
		"ori	r25, (1 << 5)			\n\t"	
		"sts	%[ctrl], r25			\n\t"	
		"sei							\n\t"
		
		"dec	r22						\n\t"	// decrement length counter
		"brne	begin%=					\n\t"	// repeat if not end of data
		
		:
		: [wip] "i" (&tx_w_index), [rip] "i" (&tx_r_index), [buffer] "i" (tx_buffer), [ctrl] "i" (_SFR_MEM_ADDR(UCSR1B))
		:);
}
//=======================================================================================
// odeslani retezce po seriovee lince
//=======================================================================================
void UART1_write_string(uint8_t *str)
{
	asm volatile (
	// copy pointer address to X reg, it may be used in indirect addressing
	"movw	r26, r24				\n\t"
	
	// load	write address into Z and aux reg 24
	"ldi	r31, hi8(%[buffer])		\n\t"	// remain same for all times
	"lds	r30, %[wip]				\n\t"
	"mov	r24, r30				\n\t"
	
	// init new store operation
	"begin%=:						\n\t"
	"inc	r24						\n\t"	// R24 = write index + 1
	
	// wait until buffer is full
	"wait%=:						\n\t"
	"lds	r25, %[rip]				\n\t"
	"cp		r24, r25				\n\t"
	"breq	wait%=					\n\t"
	
	// load data and store it to buffer
	"ld		r25, X+					\n\t"	// load byte from source
	"st		Z, r25					\n\t"	// store byte to ring buffer
	
	// new write index to ZL and store it
	"mov	r30, r24				\n\t"	// write index = next write index
	
	// atomic update
	"cli							\n\t"
	"sts	%[wip], r30				\n\t"	// store write_index
	"lds	r22, %[ctrl]			\n\t"
	"ori	r22, (1 << 5)			\n\t"
	"sts	%[ctrl], r22			\n\t"
	"sei							\n\t"
	
	"tst	r25						\n\t"	// decrement length counter
	"brne	begin%=					\n\t"	// repeat if not end of data
	
	:
	: [wip] "i" (&tx_w_index), [rip] "i" (&tx_r_index), [buffer] "i" (tx_buffer), [ctrl] "i" (_SFR_MEM_ADDR(UCSR1B))
	:);
}
//=======================================================================================
// odeslani retezce z programove pameti po seriove lince
//=======================================================================================
void UART1_write_string_P(uint32_t str)
{
	asm volatile(
		"out	0x3B, r24			\n\t"	// low byte of high word to RAMPZ
		"movw	r30, r22			\n\t"	// low word to Z register
		
		"lds	r26, %[wip]			\n\t"	// write index
		"ldi	r27, hi8(%[buffer])	\n\t"
		"mov	r25, r26			\n\t"
		
		"begin%=:					\n\t"
		"inc	r25					\n\t"
		
		"wait%=:					\n\t"
		"lds	r23, %[rip]			\n\t"
		"cp		r25, r23			\n\t"
		"breq	wait%=				\n\t"
		
		"elpm	r24, Z+				\n\t"
		"st		X, r24				\n\t"
				
		"cli						\n\t"
		"sts	%[wip], r26			\n\t"
		"lds	r26, %[ctrl]		\n\t"
		"ori	r26, (1 << 5)		\n\t"
		"sts	%[ctrl], r26		\n\t"
		"sei						\n\t"
		
		"mov	r26, r25			\n\t"
		
		"tst	r24					\n\t"
		"brne	begin%=				\n\t"
		"ret						\n\t"
		
	:
	: [wip] "i" (&tx_w_index), [rip] "i" (&tx_r_index), [buffer] "i" (tx_buffer), [ctrl] "i" (_SFR_MEM_ADDR(UCSR1B))
	:
	);
}

//=======================================================================================
// precteni jednoho bytu ze seriove linky
//=======================================================================================
uint8_t UART1_read_byte()
{
	
}

//=======================================================================================
// test, zda je prijimaci buffer prazdny
//=======================================================================================
FORCE_INLINE uint8_t UART1_is_empty()
{
	return (rx_w_index - rx_r_index);
}
//=======================================================================================
// odeslani jednoho bytu pres seriovou linku
//=======================================================================================
extern uint8_t UART1_available();
//=======================================================================================
// super optimized, use only 2 registers
ISR(USART1_UDRE_vect, ISR_NAKED)
{
	asm volatile(
		// store used registers
		"push	r30					\n\t"
		"in		r30, __SREG__		\n\t"
		"push	r30					\n\t"
		"push	r31					\n\t"
		
		//load byte from buffer and send by UART1
		"lds	r30, %[rip]			\n\t"	// buffer is aligned to 256, index is low part of address
		"ldi	r31, hi8(%[buffer])	\n\t"	// high part address
		"ld		r31, Z				\n\t"	// load byte
		"sts	%[port], r31		\n\t"	// put it to USART1
		
		// move index to next byte in buffer
		"inc	r30					\n\t"	
		"sts	%[rip], r30			\n\t"
				
		// test if this byte is last
		"lds	r31, %[wip]			\n\t"
		"cp		r30, r31			\n\t"
		"brne	end%=				\n\t"
		
		// this is last byte, disable next interrupt
		"lds	r30, %[ctrl]		\n\t"	
		"andi	r30, ~(1 << 5)		\n\t"
		"sts	%[ctrl], r30		\n\t"
		
		// restore registers
		"end%=:						\n\t"
		"pop	r31					\n\t"
		"pop	r30					\n\t"
		"out	__SREG__, r30		\n\t"
		"pop	r30					\n\t"
		"reti						\n\t"
	:
	: [wip] "i" (&tx_w_index), [rip] "i" (&tx_r_index), [buffer] "i" (tx_buffer), [port] "i" (_SFR_MEM_ADDR(UDR1)), [ctrl] "i" (_SFR_MEM_ADDR(UCSR1B)) 
	:
	);
}
//=======================================================================================
// high optimized isr, use only 3 registers
ISR(USART1_RX_vect)
{
	asm volatile (
		// store used registers
		"push	r30					\n\t"
		"in		r30, __SREG__		\n\t"
		"push	r30					\n\t"
		"push	r31					\n\t"
		"push	r24					\n\t"
		
		// read byte from serial
		"lds	r24, %[port]		\n\t"	// !!! always must read byte from serial port !!!
		
		// test if is space in buffer
		"lds	r30, %[wip]			\n\t"	// load write index
		"inc	r30					\n\t"	// shift to next loaction
		"lds	r31, %[rip]			\n\t"	// load read index
		"cp		r30, r31			\n\t"	// is full ?
		"breq	end%=				\n\t"	// yes, discard readed byte
		
		// store byte to buffer
		"sts	%[wip], r30			\n\t"
		"dec	r30					\n\t"	// move back
		"ldi	r31, hi8(%[buffer])	\n\t"	
		"st		Z, r24				\n\t"	// store byte to buffer
		
		// restore registers
		"end%=:						\n\t"
		"push	r24					\n\t"
		"pop	r31					\n\t"
		"pop	r30					\n\t"
		"out	__SREG__, r30		\n\t"
		"pop	r30					\n\t"
	:
	: [wip] "i" (&rx_w_index), [rip] "i" (&rx_r_index), [buffer] "i" (rx_buffer), [port] "i" (_SFR_MEM_ADDR(UDR1))
	:
	);
	
}
//=======================================================================================
#else
	#warning "Serial interface 1 not used"
#endif